
#include "../main.h"

#define CLASS Hppt


//////////////////////////////////////////////////////////////////////////////
//
static JSBool construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval){
	return JS_TRUE;
}


//////////////////////////////////////////////////////////////////////////////
//
static JSBool
http_get (JSContext *cx, uintN argc, jsval *vp) {
	int max_bytes = 100000;
	PRIntervalTime max_timeout = PR_MillisecondsToInterval(500);
	char buf[1000];
	JSString *sUrl;
	jsval v, headerfunc = JSVAL_VOID, rval;
	//JSRegExp *re;
	jsrefcount rc;
	PRAddrInfo *ai;
	PRStatus rv;
	PRFileDesc *sock;
	PRNetAddr server_address;	
	char *recv_buf, *b;
	int len, i;

	if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "S", &sUrl)) {
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	if(argc > 1)
		JS_ConvertValue(cx, JS_ARGV(cx, vp)[1], JSTYPE_FUNCTION, &headerfunc);
	

	char re[] = "^(?:([a-z]+)://)?([^:/]*)(?::([0-9]+))?(/.*)?$";
	JSObject *objRe = JS_NewRegExpObject(cx, re, strlen(re), 0);
	
	if (!JS_CallFunctionName(cx, objRe, "exec", 1, JS_ARGV(cx, vp), &v)) {
		JS_ReportError(cx, "js_ExecuteRegExp() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	JSObject *m;
	JS_ValueToObject(cx, v, &m);

	const char *scheme = geta(cx, m, 1, "");
	const char *host = geta(cx, m, 2, "localhost");
	int port = geti(cx, m, 3, 80);

	const char *path = geta(cx, m, 4, "/");
	
	rc = JS_SuspendRequest(cx);
	ai = PR_GetAddrInfoByName(host, PR_AF_INET, PR_AI_ADDRCONFIG);
	JS_ResumeRequest(cx, rc);
	
	if (!ai){
		JS_ReportError(cx, "PR_GetAddrInfoByName() failed");
		trace2("PR_GetAddrInfoByName() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	PR_EnumerateAddrInfo(NULL, ai, port, &server_address);
	PR_FreeAddrInfo(ai);
	
	rv = PR_InitializeNetAddr(PR_IpAddrNull, port, &server_address);
	if (PR_SUCCESS!=rv) {
		JS_ReportError(cx, "PR_InitializeNetAddr() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}

	rc = JS_SuspendRequest(cx);	
	sock = PR_OpenTCPSocket(PR_AF_INET);
	rv = PR_Connect(sock, &server_address, max_timeout);
	JS_ResumeRequest(cx, rc);
	
	if (PR_SUCCESS!=rv){
		JS_ReportError(cx, "PR_Connect() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	sprintf(buf, "GET %s HTTP/1.1\nHost: %s\nUser-Agent: %s\n\n", path, host, USER_AGENT_STRING);	
	
	len = PR_Send(sock, buf, strlen(buf), 0, max_timeout);
	
	if (len==-1) {
		JS_ReportError(cx, "PR_Send() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	recv_buf = (char *)malloc(max_bytes+1);
	
	rc = JS_SuspendRequest(cx);	
	len = PR_Recv(sock, recv_buf, max_bytes, 0, max_timeout);
	JS_ResumeRequest(cx, rc);
	
	PR_Close(sock);
		
	if (len == -1) {
		free(recv_buf);
		JS_ReportError(cx, "PR_Recv() failed");
		JS_SET_RVAL(cx, vp, JSVAL_VOID);
		return JS_FALSE;
	}
	
	recv_buf[len] = 0;

	for (b=recv_buf; *(b+1)!=0; ++b)
		if ((b[-2]=='\n') && (b[-1]=='\r') && (b[0]=='\n')) {
			b[-2]=0;
			++b;
			break;
		}

	if(!JSVAL_IS_VOID(headerfunc)){	
		char *c = recv_buf; while(*(c++)>=32); *(c++)=0;
		//*(recv_buf+12)=0;
		//int status_no = atoi(recv_buf+9);
		jsval args[3];
		args[0] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, recv_buf));
		args[1] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, c));
		args[2] = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, b));
		//free(recv_buf);
		//JS_SET_RVAL(cx, vp, JSVAL_VOID);
		if(!JS_CallFunctionValue(cx, JSVAL_NULL, headerfunc, 3, args, &rval)) {
			free(recv_buf);
			return JS_FALSE;
		}
	}
	
	JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, b)));

	free(recv_buf);
	return JS_TRUE;
}



static JSClass __class = {
    "Http", 0,
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};


static JSPropertySpec props[] = {
	{0}
};


static JSFunctionSpec methods[] = {
	JS_FN("get",  http_get, 1,0),
	JS_FS_END
};



void class_http_init(JSContext *cx){
	JSObject *proto = JS_InitClass(cx, JS_GetGlobalObject(cx), NULL, 
		&__class, construct, 0,
		props, methods, NULL, NULL
	);
}




